package com.qing.annotation.factory;

import com.google.auto.service.AutoService;
import com.qing.annotation.exception.IdAlreadyUsedException;
import com.qing.annotation.factory.module.SimpleFactoryAnnotatedClass;
import com.qing.annotation.factory.module.SimpleFactoryGroupedClasses;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author qing <br/>
 * 2023/9/17
 */
@AutoService(Processor.class)
public class SimpleFactoryProcessor extends AbstractProcessor {

    private final Map<String, SimpleFactoryGroupedClasses> mFactoryClasses = new LinkedHashMap<>();

    private Types mTypes;

    private Elements mElementUtils;

    private Filer mFiler;

    private Messager mMessager;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        mTypes = processingEnv.getTypeUtils();
        mElementUtils = processingEnv.getElementUtils();
        mFiler = processingEnv.getFiler();
        mMessager = processingEnv.getMessager();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotations = new LinkedHashSet<>();
        annotations.add(SimpleFactory.class.getCanonicalName());
        return annotations;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {

        // 遍历所有带有SimpleFactory注解的元素
        for (Element element : roundEnvironment.getElementsAnnotatedWith(SimpleFactory.class)) {

            // 过滤掉类型为非Class的
            if (!element.getKind().isClass()) {
                error(element, "Only Classes can be annotation with @%s", SimpleFactory.class.getSimpleName());
                return true;
            }

            // 将其转换为TypeElement
            TypeElement typeElement = (TypeElement) element;

            try {
                SimpleFactoryAnnotatedClass annotatedClass = new SimpleFactoryAnnotatedClass(typeElement);

                if (!isValidClass(annotatedClass)) {
                    // 非有效类
                    return true;
                }

                SimpleFactoryGroupedClasses groupedClasses = mFactoryClasses.get(annotatedClass.getQualifiedFactoryGroupName());

                if (groupedClasses == null) {
                    groupedClasses = new SimpleFactoryGroupedClasses(annotatedClass.getQualifiedFactoryGroupName());
                    mFactoryClasses.put(annotatedClass.getQualifiedFactoryGroupName(), groupedClasses);
                }
                // 将其加入对应的工厂类组中
                groupedClasses.add(annotatedClass);

            } catch (IllegalArgumentException e) {
                error(typeElement, e.getMessage());
                return true;
            } catch (IdAlreadyUsedException e) {
                SimpleFactoryAnnotatedClass existing = e.getExisting();
                error(typeElement, "Conflict: The class %s is annotated with @%s with id ='%s' but %s already uses the same id", typeElement.getQualifiedName().toString(), SimpleFactory.class.getSimpleName(), existing.getTypeElement().getQualifiedName().toString());
                return true;
            }
        }
        // 遍历工厂组类集合，生成工厂类
        try {
            for (SimpleFactoryGroupedClasses factoryClass : mFactoryClasses.values()) {
                factoryClass.generateCode(mElementUtils, mFiler);
            }
            mFactoryClasses.clear();
        } catch (IOException e) {
            error(null, e.getMessage());
        }

        return true;
    }

    /**
     * 判断注解类是否有效 <br>
     * 有效类型需满足: public修饰、非抽象类、需为{@link SimpleFactory#type}指定的的类型子类
     *
     * @param annotatedClass 待判断工厂注解类
     * @return true --> 有效类, false --> 非法类
     */
    private boolean isValidClass(SimpleFactoryAnnotatedClass annotatedClass) {
        TypeElement typeElement = annotatedClass.getTypeElement();

        // 非public修饰
        if (!typeElement.getModifiers().contains(Modifier.PUBLIC)) {
            error(typeElement, "The class %s is not public", typeElement.getQualifiedName().toString());
            return false;
        }

        // 抽象类
        if (typeElement.getModifiers().contains(Modifier.ABSTRACT)) {
            error(typeElement, "The class %s is abstract. You can't annotate abstract classes with @%", typeElement.getQualifiedName().toString(), SimpleFactory.class.getSimpleName());
            return false;
        }

        // 检查继承关系: 必须是@SimpleFactory.type()指定的类型子类
        TypeElement superClassElement = mElementUtils.getTypeElement(annotatedClass.getQualifiedFactoryGroupName());

        if (superClassElement.getKind().isInterface()) {
            // 父类是接口，需判断接口是否实现
            if (!typeElement.getInterfaces().contains(superClassElement.asType())) {
                error(typeElement, "The class %s annotation with @%s, must implement the interface %s", typeElement.getQualifiedName().toString(), SimpleFactory.class.getSimpleName(), annotatedClass.getQualifiedFactoryGroupName());
                return false;
            }
        } else {
            // 非接口, 继承树向上遍历查询, 判断是否为父类是否为@SimpleFactory.type()
            TypeElement currentClass = typeElement;
            while (true) {
                TypeMirror superclassType = currentClass.getSuperclass();
                if (superclassType.getKind() == TypeKind.NONE) {
                    // 父类为 object
                    error(typeElement, "The Class %s annotation with %s must inherit from %s", typeElement.getQualifiedName().toString(), SimpleFactory.class.getSimpleName());
                    return false;
                }

                if (superclassType.toString().equals(annotatedClass.getQualifiedFactoryGroupName())) {
                    break;
                }
                // 将其转换为 TypeElement 在继承树上继续向上搜索
                currentClass = (TypeElement) mTypes.asElement(superclassType);
            }
        }

        // 检查是否提供了默认公开(无参、公开)构造函数
        for (Element enclosed : typeElement.getEnclosedElements()) {
            if (enclosed.getKind() == ElementKind.CONSTRUCTOR) {
                ExecutableElement constructorElement = (ExecutableElement) enclosed;
                if (constructorElement.getParameters().size() == 0 && constructorElement.getModifiers().contains(Modifier.PUBLIC)) {
                    // 找到了默认构造函数
                    return true;
                }
            }
        }

        // 没有找到默认构造函数
        error(typeElement, "The class %s must provide an public empty arguments default constructor", typeElement.getQualifiedName().toString());
        return true;
    }

    private void error(Element element, String msg, Object... args) {
        mMessager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args), element);
    }

}
